package cn.eaay.stock.service.impl;

import cn.eaay.stock.common.constants.StockConstants;
import cn.eaay.stock.dto.JsonResult;
import cn.eaay.stock.dto.StockIndexInfoApiDTO;
import cn.eaay.stock.mapper.StockMarketIndexInfoMapper;
import cn.eaay.stock.mapper.StockMarketLogPriceMapper;
import cn.eaay.stock.pojo.StockMarketIndexInfo;
import cn.eaay.stock.pojo.StockMarketLogPrice;
import cn.eaay.stock.service.StockTimerService;
import cn.eaay.stock.utils.DateUtil;
import cn.eaay.stock.utils.IdWorker;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class StockTimerServiceImpl implements StockTimerService {

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private IdWorker idWorker;

    @Resource
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Resource
    private StockMarketLogPriceMapper stockMarketLogPriceMapper;

    /**
     * 从远程获取股票大盘数据及大盘流水数据，并存入数据库
     */
    @Override
    public void addMarketIndexInfoAndLogPrice() {
        // 获取远程数据
        JsonResult jsonResult = pullRemoteStockIndexInfo().getBody();
        Gson gson = new Gson();
        // 将数据序列化为json，在返回序列回来，不这样做会有异常: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to...
        String str = gson.toJson(jsonResult.getResult());
        // 反序列化
        List<StockIndexInfoApiDTO> data = gson.fromJson(str, new TypeToken<List<StockIndexInfoApiDTO>>() {
        }.getType());
        if (CollectionUtils.isEmpty(data)) {
            log.info("股票数据获取失败了，请检查接口是否可用!");
            throw new IllegalArgumentException("数据获取失败，请稍后重试!");
        }
        List<StockMarketIndexInfo> infos = Lists.newArrayList();
        List<StockMarketLogPrice> priceLogs = Lists.newArrayList();
        transformObject(data, infos, priceLogs);
        // 将数据添加进数据库
        stockMarketIndexInfoMapper.insertBatch(infos);
        stockMarketLogPriceMapper.insertBatch(priceLogs);
        // log.info("data infos : => {}", gson.toJson(infos));
        // log.info("data priceLogs : => {}", gson.toJson(priceLogs));
    }

    /**
     * 拉取远程股票指数信息
     */
    private ResponseEntity<JsonResult> pullRemoteStockIndexInfo() {
        // 设置请求头信息
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "APPCODE " + StockConstants.STOCK_API_APP_CODE);
        HttpEntity<Map<String, String>> httpEntity = new HttpEntity(headers);
        // 发送请求获取数据
        ResponseEntity<JsonResult> responseEntity = restTemplate.exchange(StockConstants.STOCK_INDEX_INFO_API_URL,
                HttpMethod.GET, httpEntity,
                JsonResult.class);
        log.info("拉取数据完毕!");
        return responseEntity;
    }

    /**
     * 将 List<StockIndexInfoApiDTO> 转为  List<StockMarketIndexInfo>
     *
     * @param target    List<StockIndexInfoApiDTO> 源数据
     * @param infos     大盘数据
     * @param logPrices 大盘流水数据
     * @return
     */
    private void transformObject(List<StockIndexInfoApiDTO> target,
                                 List<StockMarketIndexInfo> infos,
                                 List<StockMarketLogPrice> logPrices) {
        for (StockIndexInfoApiDTO dto : target) {
            // 获取上个交易日收盘价格
            BigDecimal preClosePrice = dto.getLastclosingprice();
            // 股票趋势列表
            List<String> trendList = dto.getTrend();
            for (String trend : trendList) {
                collectMarketIndexInfo(trend, preClosePrice, dto, infos);
            }
            collectMarketLogPrice(logPrices, dto);
        }
    }

    /**
     * 收集大盘指数数据
     *
     * @param trend         股票趋势字符串，按照逗号分割
     * @param preClosePrice 前交易日收盘价
     * @param dto           股票大盘数据DTO
     * @param infos         用于存储收集后的数据
     */
    public void collectMarketIndexInfo(String trend, BigDecimal preClosePrice, StockIndexInfoApiDTO dto, List<StockMarketIndexInfo> infos) {
        String[] trendArr = trend.split(",");
        // 获取当前时间
        Date curTime = null;
        try {
            curTime = DateUtil.formatFullDate(trendArr[0]);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        // 获取当前点数
        BigDecimal curPoint = new BigDecimal(trendArr[1]);
        // 计算当前价格
        BigDecimal curPrice = curPoint.subtract(preClosePrice);
        // 计算涨跌幅
        BigDecimal upDownRate = (curPrice.subtract(preClosePrice)).divide(preClosePrice, 4, RoundingMode.HALF_UP);
        // 获取成交量
        String tradeAccount = trendArr[3];
        // 获取成交额
        String tradeVolume = trendArr[4];
        StockMarketIndexInfo info = StockMarketIndexInfo.builder().id(String.valueOf(idWorker.nextId()))
                .markId(transformMarkName(dto.getName(), dto.getCode()))
                .markName(dto.getName())
                .curPoint(curPoint)
                // 计算最新价格
                .currentPrice(curPrice)
                // 计算涨跌率
                .updownRate(upDownRate)
                .curTime(curTime)
                .tradeAccount(tradeAccount)
                .tradeVolume(tradeVolume)
                .build();
        infos.add(info);
    }

    /**
     * 收集大盘流水数据
     *
     * @param logPrices
     */
    public void collectMarketLogPrice(List<StockMarketLogPrice> logPrices, StockIndexInfoApiDTO dto) {
        // 大盘前收盘和今开盘数据
        StockMarketLogPrice stockMarketLogPrice = StockMarketLogPrice.builder()
                .id(String.valueOf(idWorker.nextId()))
                .marketCode(transformMarkName(dto.getName(), dto.getCode()))
                .curDate(DateUtil.timeFullDate(dto.getUpdatetime()))
                .preClosePrice(dto.getLastclosingprice())
                .openPrice(dto.getOpenningprice())
                .build();
        logPrices.add(stockMarketLogPrice);
    }

    /**
     * 转换股票指数名称
     *
     * @param markName 指数名称
     * @param markId   指数ID
     * @return
     */
    private String transformMarkName(String markName, String markId) {
        if (StringUtils.equals(markName, "深证成指")) {
            markName = StringUtils.join(StockConstants.SZ_MARKET_PREFIX_VALUE, markId);
        } else if (StringUtils.equals(markName, "上证指数")) {
            markName = StringUtils.join(StockConstants.SH_MARKET_PREFIX_VALUE, markId);
        } else {
            markName = StringUtils.join(StockConstants.QT_MARKET_PREFIX_VALUE, markId);
        }
        return markName;
    }
}
